package IA.Bicing;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import aima.search.framework.Problem;
import aima.search.framework.Search;
import aima.search.framework.SearchAgent;

public class bicingMapBoard {
	
	Bicing bicing;
	ArrayList<furgoneta> estat;
	private int seed;
	private int E, F;
	
	// Constructor
    public bicingMapBoard(int e, int b, int f, int demanda) {

    	E = e;
    	F = f;
		bicing = new Bicing (e, b, demanda, seed);
		estat = new ArrayList<furgoneta>();
		
		for(int i = 0; i < f; i++) estat.add(new furgoneta(-1,-1,-1,0,0));
	}
    
    public bicingMapBoard(int e, int f, Bicing b, ArrayList<furgoneta> furgo) {
    	E = e;
    	F = f;
		bicing = b;
		estat = new ArrayList<furgoneta>();
		copiarFurgonetes(furgo);
	}
    
    public bicingMapBoard(int e, int b, int f, int demanda, int seed) {

    	E = e;
    	F = f;
		bicing = new Bicing (e, b, demanda, seed);
		estat = new ArrayList<furgoneta>();
		this.seed = seed;
		for(int i = 0; i < f; i++) estat.add(new furgoneta(-1,-1,-1,0,0));
	}

    private void copiarFurgonetes(ArrayList<furgoneta> f){
    	for(int i = 0; i < f.size(); i++){
    		furgoneta fu = new furgoneta(f.get(i).getOrigen(),f.get(i).getDest1(),f.get(i).getDest2(),f.get(i).getBicis1(),f.get(i).getBicis2());
    		estat.add(fu);
    	}
    }

	/**************************** OPERADORS *******************************/
	
	
	public void canviarOrigen(int f, int e){
		estat.get(f).setOrigen(e);
		int bicisNoves = bicing.getStationDoNotMove(e);
		if(bicisNoves < estat.get(f).getBicis1()+estat.get(f).getBicis2()){
			int m = bicisNoves/2;
			int n = bicisNoves - m;
			estat.get(f).setBicis1(m);
			estat.get(f).setBicis2(n);
		}
	}

	public void canviarDesti1(int f, int e){
		estat.get(f).setDest1(e);
	}

	public void canviarDesti2(int f, int e){
		estat.get(f).setDest2(e);
	}

	public void noMoure(int f, boolean d1, boolean d2){

		if(!d1){
			estat.get(f).setDest1(-1);
			estat.get(f).setDest2(-1);		
		} else if(!d2){
			estat.get(f).setDest2(-1);
		}

	}
	
	// Pre: 'n' ha de ser menor o igual al total de bicis que porta la furgoneta
	public void canviarBicis(int f, int d1, int d2){
		estat.get(f).setBicis1(d1);
		estat.get(f).setBicis2(d2);

	} 

	
	public void swapOrigen(int i, int j) {
		int aux = estat.get(i).getOrigen();
		estat.get(i).setOrigen(estat.get(j).getOrigen());
		estat.get(j).setOrigen(aux);
		
		int bicisNovesI = bicing.getStationDoNotMove(estat.get(i).getOrigen());
		if(bicisNovesI < estat.get(i).getBicis1()+estat.get(i).getBicis2()){
			int m = bicisNovesI/2;
			int n = bicisNovesI - m;
			estat.get(i).setBicis1(m);
			estat.get(i).setBicis2(n);
		}
		int bicisNovesJ = bicing.getStationDoNotMove(estat.get(j).getOrigen());
		if(bicisNovesJ < estat.get(j).getBicis1()+estat.get(j).getBicis2()){
			int m = bicisNovesJ/2;
			int n = bicisNovesJ - m;
			estat.get(j).setBicis1(m);
			estat.get(j).setBicis2(n);
		}
	}
	
	public void swapDest1(int i, int j){
		int aux = estat.get(i).getDest1();
		estat.get(i).setDest1(estat.get(j).getDest1());
		estat.get(j).setDest1(aux);
	}
	
	public void swapDest2(int i, int j){
		int aux = estat.get(i).getDest2();
		estat.get(i).setDest2(estat.get(j).getDest2());
		estat.get(j).setDest2(aux);
	}
	
	/**************************** CONSTR. ESTAT INICIAL *******************************/

	
	public void estatInicialAleatori(){

		Random generator = new Random();
		
		for(int i = 0; i < E && i < F; i++){

			estat.get(i).setOrigen(i);
			
			int est = generator.nextInt(E);
			estat.get(i).setDest1(est);
			int est2 = generator.nextInt(E);
			estat.get(i).setDest2(est2);
			
			int bic_total = bicing.getStationDoNotMove(i);
			bic_total = min(30, bic_total);
			int b1 = 0, b2 = 0;
			if(bic_total > 0){
				b1 = generator.nextInt(bic_total);
				if(bic_total-b1 > 0){
					b2 = generator.nextInt(bic_total-b1);
				}
			}
			
			
			estat.get(i).setBicis1(b1);
			estat.get(i).setBicis2(b2);
			
		}
	}
	
	public void estatInicialGreedy(){
		ArrayList<Integer> estReceptores = new ArrayList<Integer>();
		ArrayList<Integer> estDonadores = new ArrayList<Integer>();
		
		for(int i = 0; i < E; i++){
			int dif = obteDifBicis(bicing.getStationDoNotMove(i), bicing.getDemandNextHour(i), bicing.getStationNextState(i));
			//System.out.println("DIF: "+dif);
			// Falta identificador d'estacions!!
			if(dif > 0){
				estDonadores.add(i);
			} else if(dif < 0){
				estReceptores.add(i);
			}
		}
		
		ordenar(estDonadores, true);
		ordenar(estReceptores, false);
		
		int bic2Don = 0;
		int bic2Rec = 0;
		int furgo = 0;
		boolean primerRec = true;
		while(estReceptores.size() > 0 && estDonadores.size() > 0 && furgo < F){
			int bicis_don = min(30, obteDifBicis(bicing.getStationDoNotMove(estDonadores.get(0)), bicing.getDemandNextHour(estDonadores.get(0)), bicing.getStationNextState(estDonadores.get(0))));
			int bicis_rec = -obteDifBicis(bicing.getStationDoNotMove(estReceptores.get(0)), bicing.getDemandNextHour(estReceptores.get(0)), bicing.getStationNextState(estReceptores.get(0)));
			
			if(bicis_don - bic2Don > bicis_rec - bic2Rec){
				estat.get(furgo).setOrigen(estDonadores.get(0));
				if(primerRec){							// Primer viatge
					estat.get(furgo).setDest1(estReceptores.get(0));
					estat.get(furgo).setBicis1(bicis_rec - bic2Rec);
					bic2Don = bicis_rec - bic2Rec;
					primerRec = false;
				} else {								// Segon viatge
					estat.get(furgo).setDest2(estReceptores.get(0));
					estat.get(furgo).setBicis2(bicis_rec - bic2Rec);
					estDonadores.remove(0);
					furgo++;
					primerRec = true;
					bic2Don = 0;
				}
				estReceptores.remove(0);
				bic2Rec = 0;
			} else if(bicis_don - bic2Don < bicis_rec - bic2Rec){
				estat.get(furgo).setOrigen(estDonadores.get(0));
				if(primerRec){							// Primer viatge
					estat.get(furgo).setDest1(estReceptores.get(0));
					estat.get(furgo).setBicis1(bicis_don);
					primerRec = false;
					bic2Rec = bicis_don - bic2Don;
				} else {								// Segon viatge
					estat.get(furgo).setDest2(estReceptores.get(0));
					estat.get(furgo).setBicis2(bicis_don - bic2Don);
					furgo++;
					primerRec = true;
					bic2Rec = bicis_don - bic2Don;
					bic2Don = 0;
				}
				estDonadores.remove(0);
			} else {
				estat.get(furgo).setOrigen(estDonadores.get(0));
				if(primerRec){		// Primer viatge
					estat.get(furgo).setDest1(estReceptores.get(0));
					estat.get(furgo).setBicis1(bicis_don - bic2Don);
					primerRec = false;
				} else {
					estat.get(furgo).setDest2(estReceptores.get(0));
					estat.get(furgo).setBicis2(bicis_don - bic2Don);
					furgo++;
					primerRec = true;
					bic2Don = 0;
				}
				
				estReceptores.remove(0);
				estDonadores.remove(0);
				bic2Rec = 0;
			}
			
		}
		
		// Anar assignant donadores a receptores, tenint en compte el nombre de furgonetes i el m�xim de bicis perm�s
		
	}
	
	@SuppressWarnings("unchecked")
	private void ordenar(ArrayList<Integer> list, final boolean asc_desc){
		
        Collections.sort(list, new Comparator(){
        	 
            public int compare(Object o1, Object o2) {
            	
                int e1 = (Integer) o1;
                int e2 = (Integer) o2;
                
                int b1 = obteDifBicis(bicing.getStationDoNotMove(e1), bicing.getDemandNextHour(e1), bicing.getStationNextState(e1));
                int b2 = obteDifBicis(bicing.getStationDoNotMove(e2), bicing.getDemandNextHour(e2), bicing.getStationNextState(e2));
                
                if(asc_desc)	return b2-b1;
                else			return b1-b2;
            }
 
        });
	}
	
	
	private int obteDifBicis(int disp_ara, int demanda, int disp_desp){
		if(disp_desp - demanda > 0)		return min(disp_ara, max(0, disp_desp - demanda)); 	// Sobren bicis
		return disp_desp - demanda;		// Falten 0 o mes bicis
	}
	
	private int min(int a, int b){
		if(a > b)	return b;
		return a;
	}
	
	private int max(int a, int b){
		if(a > b)	return a;
		return b;
	}
	
	/**************************** ALTRES *******************************/
	
	public int getBicis(int furgo){
		return estat.get(furgo).getBicis1()+estat.get(furgo).getBicis1();
	}
	
	public int getNumFurgonetes(){
		return F;
	}	

	public int getNumEstacions(){
		return E;
	}

	public Bicing getBicing() {
		return this.bicing;
	}
	
	public int getEstacioOrigen(int furgo){
		return estat.get(furgo).getOrigen();
	}
	
	/**************************** HEURISTIC *******************************/
	
	public ArrayList<Integer> bicisPortades() {
		ArrayList<Integer> sumaBicis = new ArrayList<Integer>();	
		for (int k = 0; k < bicing.getNumStations(); k++) sumaBicis.add(0); //inicialitzem a 0 el vector
		int estac1, estac2;
		int nBicis1 = 0, nBicis2 = 0;
		
		for (int i = 0; i < estat.size(); i++) {
			if (estat.get(i).getOrigen() != -1) { //hi ha alguna furgo assignada
				estac1 = estat.get(i).getDest1(); // id estacio 1
				estac2 = estat.get(i).getDest2(); // id estacio 2				
				if (estac1 != -1) { // va a almenys 1 desti
					nBicis1 = estat.get(i).getBicis1() + sumaBicis.get(estac1);
					sumaBicis.set(estac1,nBicis1); // nBicis[estac1] += estat[i].getBicis1()		
					if (estac2 != -1) { //va a 2 destins
						nBicis2 = estat.get(i).getBicis2() + sumaBicis.get(estac2);
						sumaBicis.set(estac2,nBicis2); // nBicis[estac2] += estat[i].getBicis2()
					}
				}
			}
		}

		return sumaBicis;
	}
	
	public ArrayList<Integer> bicisAgafades() {
		ArrayList<Integer> sumaBicis = new ArrayList<Integer>();
		for (int k = 0; k < bicing.getNumStations(); k++) sumaBicis.add(0); //inicialitzem a 0 el vector
		int origen;
		int estac1, estac2;
		int nBicis1 = 0, nBicis2 = 0;
		
		for (int i = 0; i < estat.size(); i++) {
			origen = estat.get(i).getOrigen();
			if (origen != -1) { //hi ha alguna furgo assignada
				estac1 = estat.get(i).getDest1(); // id estacio 1
				estac2 = estat.get(i).getDest2(); // id estacio 2				
				if (estac1 != -1) { // almenys va a 1 desti
					nBicis1 = estat.get(i).getBicis1() + sumaBicis.get(origen);
					sumaBicis.set(origen,nBicis1); // nBicis[estac1] += estat[i].getBicis1()		
					if (estac2 != -1) { // va a 2 destions
						nBicis2 = estat.get(i).getBicis2() + sumaBicis.get(origen);
						sumaBicis.set(origen,nBicis2); // nBicis[estac2] += estat[i].getBicis2()
					}
				}
			}
		}
		return sumaBicis;
	}
	
	/**************************** GET / SET *******************************/
	
	public int getE(){
		return this.E;
	}
	
	
	public int getF(){
		return this.F;
	}

	public ArrayList<furgoneta> getEstat(){
		return this.estat;
	}
	
	public void mostraEstat(bicingMapBoard bMB){
		ArrayList<furgoneta> estat = bMB.getEstat();
		for(int i = 0; i < estat.size(); i++){
			System.out.println("\n- Furgoneta "+i);
			System.out.println("Origen "+ estat.get(i).getOrigen());
			System.out.println("Desti1 "+ estat.get(i).getDest1());
			System.out.println("Desti2 "+ estat.get(i).getDest2());
			System.out.println("Bicis1 "+ estat.get(i).getBicis1());
			System.out.println("Bicis2 "+ estat.get(i).getBicis2());
		}
	}
	
	public int getEstatSize() {
		return estat.size();
	}


}






